package  
{
	import flash.display.LoaderInfo;
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	import flash.display.Loader;
	import mx.core.FlexGlobals;
	import flash.display.DisplayObject;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.system.LoaderContext;
	
	[Event(name="currentClassChanged", type="TabDataEvent")]
	[Event(name="currentClassFailed", type="TabDataEvent")]
	[Event(name="currentClassFailed", type="cloneReady")]
	
	/**
	 * Tab data keeper
	 */
	public class TabData extends EventDispatcher
	{
		////////////////////////////////////////////////////
		// 
		////////////////////////////////////////////////////
		
		private var _title:String;
		private var _className:String;
		private var _loaderInfo:LoaderInfo;
		private var _rawData:ByteArray;
		
		private var _baseClip:MovieClip;
		private var _clips:Dictionary = new Dictionary();
		private var _currentClip:MovieClip;
		
		private var _cloneLoader:Loader;
		
		////////////////////////////////////////////////////
		// 
		////////////////////////////////////////////////////
		
		/**
		 * Tab data keeper
		 * @param	title		tab title
		 * @param	clip		root SWF MovieClip
		 * @param	loaderInfo	loader info
		 * @param	rawData		byte array with downloaded raw SWF data
		 */
		public function TabData(title:String, clip:MovieClip, loaderInfo:LoaderInfo, rawData:ByteArray=null)
		{
			_title = title;
			_currentClip = _baseClip = clip;
			_loaderInfo = loaderInfo;
			_rawData = rawData;
		}
		
		/**
		 * Tab title
		 */
		public function get title():String
		{
			return _title;
		}
		
		[Bindable (event="currentClassChanged")]
		/**
		 * Current object instance, finded by className, or null, if that class isn't exists in loaderInfo
		 */
		public function get instance():MovieClip
		{
			return _currentClip;
		}
		
		/**
		 * Class name for new current class. You can get instance of this class with instance property.
		 * If null or zero string, stageName is executed
		 */
		public function set className(value:String):void
		{
			var clipKeeper:Object;
			if (!value)
			{
				clipKeeper = { clip: _baseClip };
			}
			else
			{
				clipKeeper = _clips[value];
				if (!clipKeeper)
				{
					try {
						var cls:Class = _loaderInfo.applicationDomain.getDefinition(value) as Class;
						_clips[value] = clipKeeper = { clip: new cls() as MovieClip };
					}
					catch (e:Error) {
						dispatchEvent(new TabDataEvent(TabDataEvent.CURRENT_CLASS_FAILED, value));
						return;
					}
				}
			}
			
			if (clipKeeper.clip === _currentClip)
				return;
			
			_className = value;
			_currentClip = clipKeeper.clip;
			dispatchEvent(new TabDataEvent(TabDataEvent.CURRENT_CLASS_CHANGED, value));
		}
		
		public function get className():String
		{
			return _className ? _className : _title;
		}
		
		/**
		 * Make unique instance of current clip class
		 */
		public function cloneCurrentClip():void
		{
			if (_currentClip)
			{
				if (_className)
				{
					// Library clip is current
					
					var clip:MovieClip;
					
					try {
						var cls:Class = _loaderInfo.applicationDomain.getDefinition(_className) as Class;
						clip = new cls() as MovieClip;
					}
					catch (e:Error) {
					}
					
					dispatchEvent(new TabDataEvent(TabDataEvent.CLONE_READY, _className, clip));
				}
				else if (!_cloneLoader)
				{
					// Entire clip is current
					
					if (_rawData)
					{
						_cloneLoader = new Loader();
						
						_cloneLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, clone_completeHandler);
						_cloneLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, clone_completeHandler);
						_cloneLoader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, clone_completeHandler);
						
						var context:LoaderContext = new LoaderContext(false, DisplayObject(FlexGlobals.topLevelApplication).loaderInfo.applicationDomain, null);
						context.allowLoadBytesCodeExecution = true;
						_cloneLoader.loadBytes(_rawData, context);
					}
					else
					{
						dispatchEvent(new TabDataEvent(TabDataEvent.CLONE_READY, _title));
					}
				}
			}
			else
			{
				dispatchEvent(new TabDataEvent(TabDataEvent.CLONE_READY, className));
			}
		}
		
		/**
		 * Clone of clip complete
		 * @param	event	event
		 */
		private function clone_completeHandler(event:Event):void
		{
			_cloneLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, clone_completeHandler);
			_cloneLoader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, clone_completeHandler);
			_cloneLoader.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, clone_completeHandler);
			
			var clip:MovieClip;
			
			if (event.type == Event.COMPLETE)
			{
				if (_cloneLoader.numChildren > 0)
					clip = _cloneLoader.getChildAt(0) as MovieClip;
			}
			
			dispatchEvent(new TabDataEvent(TabDataEvent.CLONE_READY, _title, clip));
			_cloneLoader = null;
		}
	}
}